import { resources, assetManager, Asset, SpriteFrame, Sprite, Node, Prefab, instantiate, JsonAsset, error as ccError, log, warn, SpriteAtlas, TextAsset, Texture2D, ImageAsset } from 'cc';
// @ts-ignore
import { sp } from 'cc';
import { BundleManager } from './BundleManager';
import { GlobalManager } from '../global/GlobalManager';
import { Animation, AnimationClip } from 'cc';

export type ResOpt = {
    name: string;         // 资源主名（如 "role/hero"）
    bundle?: string;      // bundle名（可选）
    remote?: boolean;     // 是否远程（可选）
    type?: string;        // 图片类型，默认"png"
    ext?: string;         // 资源扩展名（如".json"，优先级高于type）
};

// 新增序列帧动画自动播放方法类型定义
export type FrameAnimOpt = {
    node: Node;
    name: string; // 合图名或散图前缀，必选
    bundle?: string;
    remote?: boolean;
    fps?: number; // 帧率，默认12
    loop?: boolean;
    onStart?: () => void;
    onEnd?: () => void;
    pattern?: string; // 散图模式，如 "effect/explode_{i}"
    count?: number;   // 散图帧数
    frames?: string[]; // 散图所有帧路径
};

export class ResourceManager {
    private static _instance: ResourceManager;
    public static get instance(): ResourceManager {
        if (!this._instance) {
            this._instance = new ResourceManager();
        }
        return this._instance;
    }
    private constructor() {
        // 删除与核心功能无关的log、warn、debug等打印，保留ccError和必要调试
    }

    private cache: Map<string, any> = new Map();
    private bundleCache: Map<string, any> = new Map();

    // 路径拼接
    private getUrl(opt: ResOpt, ext: string): string {
        // 删除与核心功能无关的log、warn、debug等打印，保留ccError和必要调试
        
        if (opt.remote) {
            if (opt.name.startsWith('http')) {
                return opt.name;
            }
            const remoteUrl = `${GlobalManager.serverUrl}${opt.name}${ext}`;
            return remoteUrl;
        }
        // 本地资源不拼接后缀
        return opt.name;
    }

    // 获取 bundle
    private async getBundle(opt: ResOpt): Promise<any> {
        // 删除与核心功能无关的log、warn、debug等打印，保留ccError和必要调试
        
        if (!opt.bundle) {
            return resources;
        }
        
        let key = opt.bundle;
        if (opt.remote) {
            key = GlobalManager.getRemoteBundleUrl(opt.bundle);
        } else {
            // 删除与核心功能无关的log、warn、debug等打印，保留ccError和必要调试
        }
        
        if (this.bundleCache.has(key)) {
            return this.bundleCache.get(key);
        }
        
        const bundle = await BundleManager.instance.loadBundle(key);
        this.bundleCache.set(key, bundle);
        return bundle;
    }

    // 加载图片（SpriteFrame）
    public async loadImg(node: Node, opt: ResOpt & { frame?: string }, cb: (sf: SpriteFrame) => void) {
        // 支持合图帧加载（自动适配bundle和frame字段）
        if (opt.frame) {
            const bundle = await this.getBundle(opt);
            bundle.load(opt.name, SpriteAtlas, (err: Error | null, atlas: SpriteAtlas) => {
                if (err || !atlas) {
                    ccError(`[ResourceManager] loadImg - 合图加载失败! URL: ${opt.name}, 错误:`, err);
                    cb && cb(null);
                    return;
                }
                const sf = atlas.getSpriteFrame(opt.frame);
                if (!sf) {
                    ccError(`[ResourceManager] loadImg - 合图中未找到帧: ${opt.frame}`);
                    cb && cb(null);
                    return;
                }
                let spComp = node && node.isValid ? node.getComponent(Sprite) : null;
                if (!node || !node.isValid) {
                    return;
                }
                if (!spComp) {
                    spComp = node.addComponent(Sprite);
                }
                spComp.spriteFrame = sf;
                cb && cb(sf);
            });
            return;
        }
        // 自动适配 SpriteFrame 路径
        let name = opt.name;
        if (!opt.remote) { // 只对本地资源补全 /spriteFrame
            if (!name.endsWith('/spriteFrame')) {
                name = name.replace(/\.(png|jpg|jpeg|webp)$/i, ''); // 去掉图片扩展名
                name = name + '/spriteFrame';
            }
            opt = { ...opt, name };
        }
        
        const ext = opt.ext || '.' + (opt.type || 'png');
        const url = this.getUrl(opt, ext);
        const cacheKey = `img:${opt.bundle || 'resources'}:${url}`;
        
        if (this.cache.has(cacheKey)) {
            if (!node || !node.isValid) {
                return;
            }
            const sf = this.cache.get(cacheKey);
            let spComp = node.getComponent(Sprite);
            if (!spComp) {
                spComp = node.addComponent(Sprite);
            }
            spComp.spriteFrame = sf;
            cb && cb(sf);
            return;
        }
        
        const bundle = await this.getBundle(opt);
        
        if (opt.remote) {
            // 远程图片用ImageAsset加载
            assetManager.loadRemote(url, ImageAsset, (err, imageAsset) => {
                if (err || !imageAsset) {
                    ccError(`[ResourceManager] loadImg - 远程图片加载失败! URL: ${url}, 错误:`, err);
                    cb && cb(null);
                    return;
                }
                const texture = new Texture2D();
                texture.image = imageAsset as ImageAsset;
                const sf = new SpriteFrame();
                sf.texture = texture;
                this.cache.set(cacheKey, sf);
                if (!node || !node.isValid) {
                    return;
                }
                let spComp = node.getComponent(Sprite);
                if (!spComp) {
                    spComp = node.addComponent(Sprite);
                }
                spComp.spriteFrame = sf;
                cb && cb(sf);
            });
            return;
        }
        
        const loadFunc = bundle.load.bind(bundle);
        
        loadFunc(url, SpriteFrame, (err: Error | null, sf: SpriteFrame) => {
            if (err) {
                ccError(`[ResourceManager] loadImg - 加载失败! URL: ${url}, 错误:`, err);
                cb && cb(null);
                return;
            }
            
            this.cache.set(cacheKey, sf);
            if (!node || !node.isValid) {
                return;
            }
            let spComp = node.getComponent(Sprite);
            if (!spComp) {
                spComp = node.addComponent(Sprite);
            }
            spComp.spriteFrame = sf;
            cb && cb(sf);
        });
    }

    // 加载 spine 动画（自动适配本地/远程，支持bundle）
    public async loadSpine(node: Node, opt: ResOpt, anim?: string, loop?: boolean, cb?: (sk: any) => void) {
        const base = opt.name;
        const cacheKey = `spine:${opt.bundle || 'resources'}:${base}`;

        if (this.cache.has(cacheKey)) {
            let skComp = node && node.isValid ? node.getComponent(sp.Skeleton) : null;
            if (!node || !node.isValid) {
                return;
            }
            if (!skComp) {
                skComp = node.addComponent(sp.Skeleton);
            }
            skComp.skeletonData = this.cache.get(cacheKey);
            if (anim) {
                skComp.setAnimation(0, anim, !!loop);
            }
            cb && cb(skComp);
            return;
        }

        // 远程资源加载
        if (opt.remote) {
            const jsonUrl = this.getUrl(opt, '.json');
            const atlasUrl = this.getUrl(opt, '.atlas');
            const pngUrl = this.getUrl(opt, '.png');
            assetManager.loadAny([{ url: atlasUrl, ext: '.txt' }, { url: jsonUrl, ext: '.txt' }], (errAny, assets) => {
                if (errAny || !assets || assets.length < 2) {
                    ccError(`[ResourceManager] loadSpine - 远程atlas/json加载失败!`, errAny);
                    cb && cb(null);
                    return;
                }
                assetManager.loadRemote(pngUrl, (errRemote, img: ImageAsset) => {
                    if (errRemote || !img) {
                        ccError(`[ResourceManager] loadSpine - 远程图片加载失败!`, errRemote);
                        cb && cb(null);
                        return;
                    }
                    let texture = new Texture2D();
                    texture.image = img as ImageAsset;
                    let skData = new sp.SkeletonData();
                    skData.skeletonJson = assets[1];
                    skData.atlasText = assets[0];
                    skData.textures = [texture];
                    skData.textureNames = [pngUrl.split('/').pop()];
                    skData._uuid = jsonUrl;
                    this.cache.set(cacheKey, skData);
                    if (!node || !node.isValid) {
                        return;
                    }
                    let skComp = node.getComponent(sp.Skeleton);
                    if (!skComp) {
                        skComp = node.addComponent(sp.Skeleton);
                    }
                    skComp.skeletonData = skData;
                    if (anim) {
                        skComp.setAnimation(0, anim, !!loop);
                    }
                    cb && cb(skComp);
                });
            });
            return;
        }

        // 本地资源加载
        const bundle = await this.getBundle(opt);
        bundle.load(base, sp.SkeletonData, (err, skData) => {
            if (err || !skData) {
                ccError(`[ResourceManager] loadSpine - 本地SkeletonData加载失败!`, err);
                cb && cb(null);
                return;
            }
            this.cache.set(cacheKey, skData);
            if (!node || !node.isValid) {
                return;
            }
            let skComp = node.getComponent(sp.Skeleton);
            if (!skComp) {
                skComp = node.addComponent(sp.Skeleton);
            }
            skComp.skeletonData = skData;
            if (anim) {
                skComp.setAnimation(0, anim, !!loop);
            }
            cb && cb(skComp);
        });
    }

    // 加载序列帧动画（SpriteFrame[]），支持合图和散图
    public async loadFrames(node: Node, opt: FrameAnimOpt | ResOpt, animName?: string, loop?: boolean, cb?: (frames: SpriteFrame[]) => void) {
        
        // 散图 pattern 模式
        if ('pattern' in opt && opt.pattern && opt.count) {
            
            const bundle = await this.getBundle(opt as ResOpt);
            
            const frames: SpriteFrame[] = [];
            let loaded = 0;
            for (let i = 0; i < opt.count; i++) {
                let url = opt.pattern.replace('{i}', String(i));
                // 只对本地资源自动补全 /spriteFrame 后缀
                if (!opt.remote && !url.endsWith('/spriteFrame')) {
                    url = url + '/spriteFrame';
                }
                if (opt.remote) {
                    // 只支持远程单帧图片，不再支持远程合图
                    // 统一用getUrl拼接url，保证和loadImg一致
                    const frameOpt = { ...opt, name: url };
                    const remoteUrl = this.getUrl(frameOpt, (opt as ResOpt).ext || '.png');
                    assetManager.loadRemote(remoteUrl, ImageAsset, (err, imageAsset) => {
                        if (err || !imageAsset) {
                            ccError(`[ResourceManager] loadFrames - 第${i}帧远程加载失败! URL: ${remoteUrl}, 错误:`, err);
                        } else {
                            const texture = new Texture2D();
                            texture.image = imageAsset as ImageAsset;
                            const sf = new SpriteFrame();
                            sf.texture = texture;
                            frames[i] = sf;
                        }
                        loaded++;
                        if (loaded === opt.count) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                } else {
                    bundle.load(url, SpriteFrame, (err, sf) => {
                        if (err) {
                            ccError(`[ResourceManager] loadFrames - 第${i}帧加载失败! URL: ${url}, 错误:`, err);
                        } else {
                        }
                        frames[i] = sf;
                        loaded++;
                        if (loaded === opt.count) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                }
            }
            return;
        }
        
        // 散图 frames 指定路径
        if ('frames' in opt && opt.frames && Array.isArray(opt.frames)) {
            
            const bundle = await this.getBundle(opt as ResOpt);
            
            const frames: SpriteFrame[] = [];
            let loaded = 0;
            for (let i = 0; i < opt.frames.length; i++) {
                const frameUrl = opt.frames[i];
                if (opt.remote) {
                    // 只支持远程单帧图片，不再支持远程合图
                    // 统一用getUrl拼接url，保证和loadImg一致
                    const frameOpt = { ...opt, name: frameUrl };
                    const remoteUrl = this.getUrl(frameOpt, (opt as ResOpt).ext || '.png');
                    assetManager.loadRemote(remoteUrl, ImageAsset, (err, imageAsset) => {
                        if (err || !imageAsset) {
                            ccError(`[ResourceManager] loadFrames - 第${i}帧远程加载失败! URL: ${remoteUrl}, 错误:`, err);
                        } else {
                            const texture = new Texture2D();
                            texture.image = imageAsset as ImageAsset;
                            const sf = new SpriteFrame();
                            sf.texture = texture;
                            frames[i] = sf;
                        }
                        loaded++;
                        if (loaded === opt.frames.length) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                } else {
                    bundle.load(frameUrl, SpriteFrame, (err, sf) => {
                        if (err) {
                            ccError(`[ResourceManager] loadFrames - 第${i}帧加载失败! URL: ${frameUrl}, 错误:`, err);
                        } else {
                        }
                        frames[i] = sf;
                        loaded++;
                        if (loaded === opt.frames.length) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                }
            }
            return;
        }
        
        // 合图（SpriteAtlas）
        const ext = (opt as ResOpt).ext || '.plist';
        const url = this.getUrl(opt as ResOpt, ext);
        const cacheKey = `frames:${opt.bundle || 'resources'}:${url}`;
        
        if (this.cache.has(cacheKey)) {
            const frames = this.cache.get(cacheKey);
            cb && cb(frames);
            return;
        }
        
        const bundle = await this.getBundle(opt as ResOpt);
        
        bundle.load(url, (err, atlas: any) => {
            if (err) {
                ccError(`[ResourceManager] loadFrames - 合图加载失败! URL: ${url}, 错误:`, err);
                cb && cb(null);
                return;
            }
            const frames = atlas.getSpriteFrames ? atlas.getSpriteFrames() : [];
            this.cache.set(cacheKey, frames);
            cb && cb(frames);
        });
    }

    // 加载 prefab
    public async loadPrefab(opt: ResOpt, cb: (prefab: Prefab) => void) {
        
        const ext = opt.ext || '.prefab';
        const url = this.getUrl(opt, ext);
        const cacheKey = `prefab:${opt.bundle || 'resources'}:${url}`;
        
        if (this.cache.has(cacheKey)) {
            cb && cb(this.cache.get(cacheKey));
            return;
        }
        
        const bundle = await this.getBundle(opt);
        
        bundle.load(url, Prefab, (err: Error | null, prefab: Prefab) => {
            if (err) {
                ccError(`[ResourceManager] loadPrefab - 加载失败! URL: ${url}, 错误:`, err);
                cb && cb(null);
                return;
            }
            this.cache.set(cacheKey, prefab);
            cb && cb(prefab);
        });
    }

    // 加载 json
    public async loadJson(opt: ResOpt, cb: (json: any) => void) {
        
        const ext = opt.ext || '.json';
        const url = this.getUrl(opt, ext);
        const cacheKey = `json:${opt.bundle || 'resources'}:${url}`;
        
        if (this.cache.has(cacheKey)) {
            cb && cb(this.cache.get(cacheKey));
            return;
        }
        
        const bundle = await this.getBundle(opt);
        
        bundle.load(url, JsonAsset, (err: Error | null, jsonAsset: JsonAsset) => {
            if (err) {
                ccError(`[ResourceManager] loadJson - 加载失败! URL: ${url}, 错误:`, err);
                cb && cb(null);
                return;
            }
            this.cache.set(cacheKey, jsonAsset.json);
            cb && cb(jsonAsset.json);
        });
    }

    // 保留原有万能加载方法，供特殊场景直接调用
    public loadRes<T extends Asset>(url: string, type: typeof Asset): Promise<T> {
        
        return new Promise((resolve, reject) => {
            resources.load(url, type, (err, asset) => {
                if (err) {
                    ccError(`[ResourceManager] loadRes - 加载失败! URL: ${url}, 错误:`, err);
                    reject(err);
                } else {
                    resolve(asset as T);
                }
            });
        });
    }

    // 释放资源
    public release(opt: ResOpt) {
        
        // 这里只清理缓存，实际资源释放可扩展
        const keys = [
            `img:${opt.bundle || 'resources'}:${this.getUrl(opt, '.' + (opt.type || 'png'))}`,
            `spine:${opt.bundle || 'resources'}:${opt.name}`,
            `frames:${opt.bundle || 'resources'}:${this.getUrl(opt, (opt as ResOpt).ext || '.plist')}`,
            `prefab:${opt.bundle || 'resources'}:${this.getUrl(opt, opt.ext || '.prefab')}`,
            `json:${opt.bundle || 'resources'}:${this.getUrl(opt, opt.ext || '.json')}`
        ];
        
        for (const k of keys) {
            if (this.cache.delete(k)) {
            }
        }
    }

    /**
     * 按bundle名清理缓存，跳过不需要清理的bundle
     */
    public releaseByBundle(bundleName: string) {
        if (GlobalManager.noCleanupBundles.has(bundleName)) {
            return;
        }
        const keysToDelete = [];
        for (const key of this.cache.keys()) {
            if (key.includes(`:${bundleName}:`)) {
                keysToDelete.push(key);
            }
        }
        for (const k of keysToDelete) {
            this.cache.delete(k);
        }
    }

    /**
     * 全部清理缓存，跳过不需要清理的bundle
     */
    public releaseAll() {
        const keysToDelete = [];
        for (const key of this.cache.keys()) {
            let skip = false;
            for (const bundleName of GlobalManager.noCleanupBundles) {
                if (key.includes(`:${bundleName}:`)) {
                    skip = true;
                    break;
                }
            }
            if (!skip) {
                keysToDelete.push(key);
            }
        }
        for (const k of keysToDelete) {
            this.cache.delete(k);
        }
    }

    // 新增序列帧动画自动播放方法
    public playFrames(opt: FrameAnimOpt) {
        
        let stopped = false, paused = false, timer = null;
        let nextFrame = null;
        this.loadFrames(opt.node, opt, undefined, opt.loop, (frames: SpriteFrame[]) => {
            // 过滤掉加载失败的帧
            frames = frames.filter(f => !!f);
            if (!frames || frames.length === 0) {
                ccError(`[ResourceManager] playFrames - 没有获取到帧数据`);
                return;
            }
            
            let sprite = opt.node && opt.node.isValid ? opt.node.getComponent(Sprite) : null;
            if (!opt.node || !opt.node.isValid) {
                return;
            }
            if (!sprite) {
                sprite = opt.node.addComponent(Sprite);
            }
            
            let idx = 0;
            const total = frames.length;
            const interval = 1 / (opt.fps || 12);
            
            if (opt.onStart) {
                opt.onStart();
            }
            
            nextFrame = function nextFrame() {
                if (stopped) {
                    return;
                }
                if (paused) {
                    timer = setTimeout(nextFrame, interval * 1000);
                    return;
                }
                // 防御性判断
                if (!frames[idx]) {
                    return;
                }
                if (!sprite || !sprite.node || !sprite.node.isValid) {
                    return;
                }
                sprite.spriteFrame = frames[idx];
                idx++;
                if (idx >= total) {
                    if (opt.loop) {
                        idx = 0;
                        log(`[ResourceManager] playFrames - 循环播放，重置到第0帧`);
                        timer = setTimeout(nextFrame, interval * 1000);
                    } else {
                        log(`[ResourceManager] playFrames - 播放完成`);
                        if (opt.onEnd) {
                            opt.onEnd();
                        }
                        stopped = true;
                    }
                } else {
                    timer = setTimeout(nextFrame, interval * 1000);
                }
            };
            nextFrame();
        });
        return {
            stop: () => {
                stopped = true;
                if (timer) clearTimeout(timer);
            },
            pause: () => {
                paused = true;
            },
            resume: () => {
                if (paused && nextFrame) {
                    paused = false;
                    nextFrame();
                }
            }
        };
    }

    /**
     * 用 Animation 组件动态生成并播放序列帧动画
     * 支持合图和散图，支持 fps、loop、onStart/onEnd
     * opt.frames 优先，pattern+count 其次，name 合图最后
     */
    public playFramesByAnimation(opt: FrameAnimOpt) {
        
        const node = opt.node;
        const fps = opt.fps || 12;
        const loop = opt.loop !== false;
        const animName = opt.name || 'frameAnim';
        
        // 加载帧
        this.loadFrames(node, opt, undefined, loop, (frames: SpriteFrame[]) => {
            if (!frames || frames.length === 0) {
                ccError(`[ResourceManager] playFramesByAnimation - 没有获取到帧数据`);
                return;
            }
            if(!node || !node.isValid) {
                return;
            }
            if(!node.getComponent(Sprite)){
                node.addComponent(Sprite)
            }
            
            // 动态创建 AnimationClip
            const animClip = new AnimationClip();
            animClip.name = animName;
            animClip.duration = frames.length / fps;
            animClip.wrapMode = loop ? AnimationClip.WrapMode.Loop : AnimationClip.WrapMode.Normal;
            
            // 关键帧
            // @ts-ignore
            const track = new (window as any).cc.SpriteFrameTrack();
            // @ts-ignore
            track.path = new (window as any).cc.AnimationPath().toComponent('Sprite').toProperty('spriteFrame');
            track.channel.curve.assignSorted(
                frames.map((sf, i) => [i / fps, sf])
            );
            animClip.addTrack(track);
            
            // Animation 组件
            let anim = node && node.isValid ? node.getComponent(Animation) : null;
            if (!node || !node.isValid) {
                return;
            }
            if (!anim) {
                anim = node.addComponent(Animation);
            } else {
            }
            
            anim.addClip(animClip);
            
            // 事件监听
            if (opt.onStart) {
                anim.once(Animation.EventType.PLAY, opt.onStart);
            }
            if (opt.onEnd) {
                anim.once(Animation.EventType.FINISHED, opt.onEnd);
            }
            
            anim.play(animName);
        });
    }

    /**
     * 预加载单张图片（SpriteFrame）
     */
    public async preloadImg(opt: ResOpt, cb?: (sf: SpriteFrame) => void) {
        let name = opt.name;
        if (!opt.remote) {
            if (!name.endsWith('/spriteFrame')) {
                name = name.replace(/\.(png|jpg|jpeg|webp)$/i, '');
                name = name + '/spriteFrame';
            }
            opt = { ...opt, name };
        }
        const ext = opt.ext || '.' + (opt.type || 'png');
        const url = this.getUrl(opt, ext);
        const cacheKey = `img:${opt.bundle || 'resources'}:${url}`;
        if (this.cache.has(cacheKey)) {
            cb && cb(this.cache.get(cacheKey));
            return;
        }
        const bundle = await this.getBundle(opt);
        if (opt.remote) {
            assetManager.loadRemote(url, ImageAsset, (err, imageAsset) => {
                if (err || !imageAsset) {
                    ccError(`[ResourceManager] preloadImg - 远程图片加载失败! URL: ${url}, 错误:`, err);
                    cb && cb(null);
                    return;
                }
                const texture = new Texture2D();
                texture.image = imageAsset as ImageAsset;
                const sf = new SpriteFrame();
                sf.texture = texture;
                this.cache.set(cacheKey, sf);
                cb && cb(sf);
            });
        } else {
            bundle.load(url, SpriteFrame, (err: Error | null, sf: SpriteFrame) => {
                if (err) {
                    ccError(`[ResourceManager] preloadImg - 加载失败! URL: ${url}, 错误:`, err);
                    cb && cb(null);
                    return;
                }
                this.cache.set(cacheKey, sf);
                cb && cb(sf);
            });
        }
    }

    /**
     * 预加载序列帧（SpriteFrame[]，pattern/frames模式）
     */
    public async preloadFrames(opt: FrameAnimOpt | ResOpt, cb?: (frames: SpriteFrame[]) => void) {
        // pattern模式
        if ('pattern' in opt && opt.pattern && opt.count) {
            const bundle = await this.getBundle(opt as ResOpt);
            const frames: SpriteFrame[] = [];
            let loaded = 0;
            for (let i = 0; i < opt.count; i++) {
                let url = opt.pattern.replace('{i}', String(i));
                if (!opt.remote && !url.endsWith('/spriteFrame')) {
                    url = url + '/spriteFrame';
                }
                if (opt.remote) {
                    const frameOpt = { ...opt, name: url };
                    const remoteUrl = this.getUrl(frameOpt, (opt as ResOpt).ext || '.png');
                    assetManager.loadRemote(remoteUrl, ImageAsset, (err, imageAsset) => {
                        if (!err && imageAsset) {
                            const texture = new Texture2D();
                            texture.image = imageAsset as ImageAsset;
                            const sf = new SpriteFrame();
                            sf.texture = texture;
                            frames[i] = sf;
                        }
                        loaded++;
                        if (loaded === opt.count) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                } else {
                    bundle.load(url, SpriteFrame, (err, sf) => {
                        if (!err && sf) {
                            frames[i] = sf;
                        }
                        loaded++;
                        if (loaded === opt.count) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                }
            }
            return;
        }
        // frames数组模式
        if ('frames' in opt && opt.frames && Array.isArray(opt.frames)) {
            const bundle = await this.getBundle(opt as ResOpt);
            const frames: SpriteFrame[] = [];
            let loaded = 0;
            for (let i = 0; i < opt.frames.length; i++) {
                const frameUrl = opt.frames[i];
                if (opt.remote) {
                    const frameOpt = { ...opt, name: frameUrl };
                    const remoteUrl = this.getUrl(frameOpt, (opt as ResOpt).ext || '.png');
                    assetManager.loadRemote(remoteUrl, ImageAsset, (err, imageAsset) => {
                        if (!err && imageAsset) {
                            const texture = new Texture2D();
                            texture.image = imageAsset as ImageAsset;
                            const sf = new SpriteFrame();
                            sf.texture = texture;
                            frames[i] = sf;
                        }
                        loaded++;
                        if (loaded === opt.frames.length) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                } else {
                    bundle.load(frameUrl, SpriteFrame, (err, sf) => {
                        if (!err && sf) {
                            frames[i] = sf;
                        }
                        loaded++;
                        if (loaded === opt.frames.length) {
                            const validFrames = frames.filter(f => !!f);
                            cb && cb(validFrames);
                        }
                    });
                }
            }
            return;
        }
        // 合图模式（本地）
        const ext = (opt as ResOpt).ext || '.plist';
        const url = this.getUrl(opt as ResOpt, ext);
        const cacheKey = `frames:${opt.bundle || 'resources'}:${url}`;
        if (this.cache.has(cacheKey)) {
            cb && cb(this.cache.get(cacheKey));
            return;
        }
        const bundle = await this.getBundle(opt as ResOpt);
        bundle.load(url, (err, atlas: any) => {
            if (!err && atlas && atlas.getSpriteFrames) {
                const frames = atlas.getSpriteFrames();
                this.cache.set(cacheKey, frames);
                cb && cb(frames);
            } else {
                cb && cb([]);
            }
        });
    }

    /**
     * 预加载spine动画（sp.SkeletonData）
     */
    public async preloadSpine(opt: ResOpt, cb?: (skData: any) => void) {
        const base = opt.name;
        const cacheKey = `spine:${opt.bundle || 'resources'}:${base}`;
        if (this.cache.has(cacheKey)) {
            cb && cb(this.cache.get(cacheKey));
            return;
        }
        if (opt.remote) {
            const jsonUrl = this.getUrl(opt, '.json');
            const atlasUrl = this.getUrl(opt, '.atlas');
            const pngUrl = this.getUrl(opt, '.png');
            assetManager.loadAny([{ url: atlasUrl, ext: '.txt' }, { url: jsonUrl, ext: '.txt' }], (errAny, assets) => {
                if (errAny || !assets || assets.length < 2) {
                    ccError(`[ResourceManager] preloadSpine - 远程atlas/json加载失败!`, errAny);
                    cb && cb(null);
                    return;
                }
                assetManager.loadRemote(pngUrl, ImageAsset, (errRemote, img: ImageAsset) => {
                    if (errRemote || !img) {
                        ccError(`[ResourceManager] preloadSpine - 远程图片加载失败!`, errRemote);
                        cb && cb(null);
                        return;
                    }
                    let texture = new Texture2D();
                    texture.image = img as ImageAsset;
                    let skData = new sp.SkeletonData();
                    skData.skeletonJson = assets[1];
                    skData.atlasText = assets[0];
                    skData.textures = [texture];
                    skData.textureNames = [pngUrl.split('/').pop()];
                    skData._uuid = jsonUrl;
                    this.cache.set(cacheKey, skData);
                    cb && cb(skData);
                });
            });
            return;
        }
        const bundle = await this.getBundle(opt);
        bundle.load(base, sp.SkeletonData, (err, skData) => {
            if (!err && skData) {
                this.cache.set(cacheKey, skData);
                cb && cb(skData);
            } else {
                cb && cb(null);
            }
        });
    }

    /**
     * 预加载Prefab
     */
    public async preloadPrefab(opt: ResOpt, cb?: (prefab: Prefab) => void) {
        const ext = opt.ext || '.prefab';
        const url = this.getUrl(opt, ext);
        const cacheKey = `prefab:${opt.bundle || 'resources'}:${url}`;
        if (this.cache.has(cacheKey)) {
            cb && cb(this.cache.get(cacheKey));
            return;
        }
        const bundle = await this.getBundle(opt);
        bundle.load(url, Prefab, (err: Error | null, prefab: Prefab) => {
            if (!err && prefab) {
                this.cache.set(cacheKey, prefab);
                cb && cb(prefab);
            } else {
                cb && cb(null);
            }
        });
    }

    /**
     * 预加载Json
     */
    public async preloadJson(opt: ResOpt, cb?: (json: any) => void) {
        const ext = opt.ext || '.json';
        const url = this.getUrl(opt, ext);
        const cacheKey = `json:${opt.bundle || 'resources'}:${url}`;
        if (this.cache.has(cacheKey)) {
            cb && cb(this.cache.get(cacheKey));
            return;
        }
        const bundle = await this.getBundle(opt);
        bundle.load(url, JsonAsset, (err: Error | null, jsonAsset: JsonAsset) => {
            if (!err && jsonAsset) {
                this.cache.set(cacheKey, jsonAsset.json);
                cb && cb(jsonAsset.json);
            } else {
                cb && cb(null);
            }
        });
    }
} 